package com.shycloud.common.minio.service;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.processing.OperationManager;
import com.qiniu.storage.BucketManager;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.storage.model.FileInfo;
import com.qiniu.util.Auth;
import com.qiniu.util.StringMap;
import com.qiniu.util.UrlSafeBase64;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.messages.Bucket;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.Optional;

/**
 * minio 交互类
 *
 * @author npc
 */
@RequiredArgsConstructor
@Qualifier("minioTemplate")
public class MinioTemplate implements InitializingBean {

	private final String bucket;
	private final String accessKey;
	private final String secretKey;
	private final String cdn;
	private final String privateCdn;
	private final String privateBucket;
	private MinioClient client;
	private StringMap putPolicy;
	@Autowired
	private UploadManager uploadManager;

	@Autowired
	private OperationManager operationManager;

	@Autowired
	private BucketManager bucketManager;

	@Autowired
	private Auth auth;

	/**
	 * 创建bucket
	 *
	 * @param bucketName bucket名称
	 */
	@SneakyThrows
	public void createBucket(String bucketName) {
		if (!client.bucketExists(bucketName)) {
			client.makeBucket(bucketName);
		}
	}

	/**
	 * 获取全部bucket
	 * <p>
	 * https://docs.minio.io/cn/java-client-api-reference.html#listBuckets
	 */
	@SneakyThrows
	public List<Bucket> getAllBuckets() {
		return client.listBuckets();
	}

	/**
	 * @param bucketName bucket名称
	 */
	@SneakyThrows
	public Optional<Bucket> getBucket(String bucketName) {
		return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
	}

	/**
	 * @param bucketName bucket名称
	 */
	@SneakyThrows
	public void removeBucket(String bucketName) {
		client.removeBucket(bucketName);
	}

	/**
	 * 根据文件前置查询文件
	 *
	 * @param bucketName bucket名称
	 * @param prefix     前缀
	 * @param recursive  是否递归查询
	 * @return MinioItem 列表
	 */
	@SneakyThrows
	public FileInfo[] getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {

		Configuration cfg = new Configuration(Zone.zone2());

		BucketManager bucketManager = new BucketManager(auth, cfg);

		BucketManager.FileListIterator fileListIterator = bucketManager
				.createFileListIterator(bucketName, prefix, 500, "");

		//List<FileInfo> fileInfos = new ArrayList<>();
		FileInfo[] items = null;
		if (fileListIterator.hasNext()) {
			//处理获取的file list结果
			items = fileListIterator.next();
		}
		return items;
	}

	/**
	 * 获取文件外链
	 *
	 * @param bucketName bucket名称
	 * @param objectName 文件名称
	 * @param expires    过期时间 <=7
	 * @return url
	 */
	@SneakyThrows
	public String getObjectURL(String bucketName, String objectName, Integer expires) {
		return client.presignedGetObject(bucketName, objectName, expires);
	}

	/**
	 * 获取文件
	 *
	 * @param bucketName bucket名称
	 * @param objectName 文件名称
	 * @return 二进制流
	 */
	@SneakyThrows
	public InputStream getObject(String bucketName, String objectName) {
		return client.getObject(bucketName, objectName);
	}

	/**
	 * 上传文件
	 *
	 * @param bucketName bucket名称
	 * @param objectName 文件名称
	 * @param stream     文件流
	 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
	 */
	public void putObject(String bucketName, String objectName, InputStream stream)
			throws Exception {
		client.putObject(bucketName, objectName, stream, stream.available(),
				"application/octet-stream");
	}

	/**
	 * 上传文件
	 *
	 * @param bucketName  bucket名称
	 * @param objectName  文件名称
	 * @param stream      文件流
	 * @param size        大小
	 * @param contextType 类型
	 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#putObject
	 */
	public void putObject(String bucketName, String objectName, InputStream stream, long size,
			String contextType) throws Exception {
		client.putObject(bucketName, objectName, stream, size, contextType);
	}

	/**
	 * 获取文件信息
	 *
	 * @param bucketName bucket名称
	 * @param objectName 文件名称
	 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
	 */
	public ObjectStat getObjectInfo(String bucketName, String objectName) throws Exception {
		return client.statObject(bucketName, objectName);
	}

	/**
	 * 删除文件
	 *
	 * @param bucketName bucket名称
	 * @param objectName 文件名称
	 * @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#removeObject
	 */
	public Response removeObject(String bucketName, String objectName) throws Exception {

		//构造一个带指定 Region 对象的配置类
		Configuration cfg = new Configuration(Zone.zone2());

		BucketManager bucketManager = new BucketManager(auth, cfg);
		try {
			return bucketManager.delete(bucketName, objectName);
		} catch (QiniuException ex) {
			//如果遇到异常，说明删除失败
			System.err.println(ex.code());
			System.err.println(ex.response.toString());
			return ex.response;
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		this.putPolicy = new StringMap();
		putPolicy.put("returnBody",
				"{\"key\":\"$(key)\",\"hash\":\"$(etag)\",\"bucket\":\"$(bucket)\",\"width\":$(imageInfo.width), \"height\":${imageInfo.height}}");
		putPolicy.put("insertOnly", 0);
	}

	/**
	 * 七牛云上传文件
	 *
	 * @param file 文件
	 * @param key  文件Key
	 * @return
	 */
	@SneakyThrows
	public String uploadFile(File file, String key) throws QiniuException {
		Response response = this.uploadManager.put(file, key, getUploadToken(key));
		int retry = 0;
		while (response.needRetry() && retry < 3) {
			response = this.uploadManager.put(file, key, getUploadToken(key));
			retry++;
		}
		//解析上传成功的结果
		DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
		String finalUrl = String.format("%s/%s", cdn, putRet.key);
		return finalUrl;
	}

	/**
	 * 七牛云上传文件
	 *
	 * @param inputStream 文件流
	 * @param key         文件Key
	 * @return
	 */
	@SneakyThrows
	public String uploadFile(InputStream inputStream, String key) throws QiniuException {
		Response response = this.uploadManager
				.put(inputStream, key, getUploadToken(key), null, null);
		int retry = 0;
		while (response.needRetry() && retry < 3) {
			response = this.uploadManager.put(inputStream, key, getUploadToken(key), null, null);
			retry++;
		}
		//解析上传成功的结果
		DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
		String finalUrl = String.format("%s/%s", cdn, putRet.key);
		return finalUrl;
	}

	/**
	 * 七牛云上传文件至指定bucket【华南节点】
	 *
	 * @param inputStream 文件流
	 * @param key         文件Key
	 * @return
	 */
	@SneakyThrows
	public String uploadFile(InputStream inputStream, String key, String bucket)
			throws QiniuException {

		Configuration cfg = new Configuration(Region.region2());
		UploadManager uploadManager = new UploadManager(cfg);
		Response response = uploadManager
				.put(inputStream, key, getUploadToken(key, bucket), null, null);
		int retry = 0;
		while (response.needRetry() && retry < 3) {
			response = uploadManager.put(inputStream, key, getUploadToken(key, bucket), null, null);
			retry++;
		}
		//解析上传成功的结果
		DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
		String finalUrl = String.format("%s/%s", cdn, putRet.key);
		return finalUrl;
	}

	@SneakyThrows
	public String uploadByte(byte[] bytes, String fileName, String bucket, String url) {

		Configuration cfg = new Configuration(Region.region2());
		UploadManager uploadManager = new UploadManager(cfg);
		Response response = uploadManager.put(bytes, fileName, getUploadToken(fileName, bucket));

		//解析上传成功的结果
		DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
		String finalUrl = String.format("%s/%s", url, putRet.key);
		return finalUrl;

	}

	@SneakyThrows
	public String uploadFileCdn(InputStream inputStream, String key, String bucket, String cdn)
			throws QiniuException {

		Configuration cfg = new Configuration(Region.region2());
		UploadManager uploadManager = new UploadManager(cfg);
		Response response = uploadManager
				.put(inputStream, key, getUploadToken(key, bucket), null, null);
		int retry = 0;
		while (response.needRetry() && retry < 3) {
			response = uploadManager.put(inputStream, key, getUploadToken(key, bucket), null, null);
			retry++;
		}
		//解析上传成功的结果
		DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
		String finalUrl = String.format("%s/%s", cdn, putRet.key);
		return finalUrl;
	}

	@SneakyThrows
	public String uploadFileWX(InputStream inputStream, String key, String bucket, String cdnUrl)
			throws QiniuException {
		Configuration cfg = new Configuration(Region.region2());
		UploadManager uploadManager = new UploadManager(cfg);
		Response response = uploadManager
				.put(inputStream, key, getUploadToken(key, bucket), null, null);
		int retry = 0;
		while (response.needRetry() && retry < 3) {
			response = uploadManager.put(inputStream, key, getUploadToken(key, bucket), null, null);
			retry++;
		}
		//解析上传成功的结果
		DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
		String finalUrl = String.format("%s/%s", cdnUrl, putRet.key);
		return finalUrl;
	}

	/**
	 * 七牛云私有空间上传文件
	 *
	 * @param file 文件
	 * @param key  文件Key
	 * @return
	 */
	@SneakyThrows
	public String privateUploadFile(File file, String key) throws QiniuException {
		Response response = this.uploadManager.put(file, key, getPrivateUploadToken(key));
		int retry = 0;
		while (response.needRetry() && retry < 3) {
			response = this.uploadManager.put(file, key, getPrivateUploadToken(key));
			retry++;
		}
		//解析上传成功的结果
		DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
		String finalUrl = String.format("%s/%s", privateCdn, putRet.key);
		return finalUrl;
	}

	/**
	 * 七牛云私有空间上传文件
	 *
	 * @param inputStream 文件流
	 * @param key         文件Key
	 * @return
	 */
	@SneakyThrows
	public String privateUploadFile(InputStream inputStream, String key) throws QiniuException {
		Response response = this.uploadManager
				.put(inputStream, key, getPrivateUploadToken(key), null, null);
		int retry = 0;
		while (response.needRetry() && retry < 3) {
			response = this.uploadManager
					.put(inputStream, key, getPrivateUploadToken(key), null, null);
			retry++;
		}
		//解析上传成功的结果
		DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
		String finalUrl = String.format("%s/%s", privateCdn, putRet.key);
		return finalUrl;
	}

	/**
	 * 七牛删除文件
	 *
	 * @param key 文件Key
	 * @return
	 */
	@SneakyThrows
	public Response delete(String key) throws QiniuException {

		Configuration cfg = new Configuration(Zone.zone2());

		BucketManager bucketManager = new BucketManager(auth, cfg);

		Response response = bucketManager.delete("mido-songbook-sourcefile", key);
		int retry = 0;
		while (response.needRetry() && retry++ < 3) {
			response = bucketManager.delete("mido-songbook-sourcefile", key);
		}
		return response;
	}

	/**
	 * 七牛批量删除文件
	 *
	 * @param keys 文件Key
	 * @return
	 */
	@SneakyThrows
	public Response deleteBatch(String bucketName, String[] keys) throws QiniuException {

		Configuration cfg = new Configuration(Zone.zone2());
		BucketManager bucketManager = new BucketManager(auth, cfg);

		BucketManager.BatchOperations batchOperations = new BucketManager.BatchOperations();

		batchOperations.addDeleteOp(bucketName, keys);

		Response response = bucketManager.batch(batchOperations);

		int retry = 0;
		while (response.needRetry() && retry++ < 3) {
			response = bucketManager.batch(batchOperations);
		}
		return response;
	}

	/**
	 * 七牛删除指定 bucket 文件
	 *
	 * @param key 文件Key
	 * @return
	 */
	@SneakyThrows
	public Response delete(String bucket, String key) throws QiniuException {

		Configuration cfg = new Configuration(Zone.zone2());

		BucketManager bucketManager = new BucketManager(auth, cfg);

		Response response = bucketManager.delete(bucket, key);
		int retry = 0;
		while (response.needRetry() && retry++ < 3) {
			response = bucketManager.delete(bucket, key);
		}
		return response;
	}

	/**
	 * 七牛私有空间删除文件
	 *
	 * @param key 文件Key
	 * @return
	 */
	@SneakyThrows
	public Response privateDelete(String key) throws QiniuException {
		Response response = bucketManager.delete(privateBucket, key);
		int retry = 0;
		while (response.needRetry() && retry++ < 3) {
			response = bucketManager.delete(privateBucket, key);
		}
		return response;
	}

	/**
	 * 获取覆盖上传凭证
	 *
	 * @return
	 */
	@SneakyThrows
	public String getUploadToken(String key) {
		String s = this.auth.uploadToken(bucket, key);
		return s;
	}

	/**
	 * 获取覆盖上传凭证(指定bucket)
	 *
	 * @return
	 */
	@SneakyThrows
	public String getUploadToken(String key, String myBucket) {
		return this.auth.uploadToken(myBucket, key);
	}

	/**
	 * 获取简单上传凭证
	 *
	 * @return
	 */
	@SneakyThrows
	public String getUploadToken() {
		return this.auth.uploadToken(bucket, null, 3600, putPolicy);
	}

//	/**
//	 * 获取加密覆盖上传凭证
//	 *
//	 * @return
//	 */
//	@SneakyThrows
//	public String getDrmUploadToken(String key) {
//		StringMap putPolicy = new StringMap();
//		//数据处理指令，支持多个指令
//		String saveMp4Entry = String.format("%s:%s", bucket, key);
//		String avthumbMp4Fop = String.format("avthumb/mp4/acodec/copy/vcodec/copy/drmComKey/%s/drmFileKey/%s|saveas/%s",
//				UrlSafeBase64.encodeToString(drmCompanyKey),
//				UrlSafeBase64.encodeToString(drmFileKey),
//				UrlSafeBase64.encodeToString(saveMp4Entry));
//		putPolicy.put("persistentOps", avthumbMp4Fop);
//		//数据处理队列名称，必填
//		putPolicy.put("persistentPipeline", "midojoy");
//		long expireSeconds = 3600;
//
//		return this.auth.uploadToken(bucket, key, expireSeconds, putPolicy);
//	}

	/**
	 * 获取私有空间覆盖上传凭证
	 *
	 * @return
	 */
	@SneakyThrows
	public String getPrivateUploadToken(String key) {
		return this.auth.uploadToken(privateBucket, key);
	}

	/**
	 * 获取私有空间覆盖上传凭证 - 指定 bucket
	 *
	 * @return
	 */
	@SneakyThrows
	public String getPrivateUploadToken(String bucket, String key) {
		return this.auth.uploadToken(bucket, key);
	}

	/**
	 * 获取私有空间覆盖上传凭证 - 指定 bucket,指定过期时间
	 *
	 * @return
	 */
	@SneakyThrows
	public String getPrivateUploadToken(String bucket, String key, long overTime) {
		return this.auth.uploadToken(bucket, key, overTime, null, true);
	}

	/**
	 * 创建上传策略
	 *
	 * @param
	 * @return com.qiniu.util.StringMap
	 * @author nianhua.jiang
	 * @date 2020/11/27 11:19
	 **/
	public StringMap createPolicy(String bucket) {
		StringMap policy = new StringMap();

		policy.put("scope", bucket);

		return policy;
	}

	/**
	 * 获取私有空间访问凭证
	 *
	 * @return
	 */
	@SneakyThrows
	public String getPrivateDownloadUrl(String url) {
		long expireInSeconds = 300;
		return auth.privateDownloadUrl(url, expireInSeconds);
	}

	/**
	 * 获取加密覆盖上传凭证
	 *
	 * @return
	 */
	@SneakyThrows
	public String setPrivateMvLogo(String key) {

		//数据处理指令，支持多个指令
		String saveMp4Entry = String.format("%s:%s", privateBucket, key);
		String avthumbMp4Fop = String.format("avthumb/mp4/wmImage/%s|saveas/%s",
				UrlSafeBase64.encodeToString("http://file.formal.joymido.com/mvLogo.png"),
				UrlSafeBase64.encodeToString(saveMp4Entry));

		String persistentPipeline = "midojoy";
		String persistentId = operationManager
				.pfop(privateBucket, key, avthumbMp4Fop, persistentPipeline, true);

		return persistentId;
	}

}
